package main

import (
	"errors"
	"fmt"
	"github.com/nsf/termbox-go"
	"github.com/spf13/cast"
	"os"
	"os/exec"
	"sync"
	"time"
)

var (
	engine   *Box
	flush    chan int
	complete chan int
)

type Box struct {
	H             int     //高度
	W             int     //宽度
	M             [][]int //地图
	Levels        int     //关卡
	L             *sync.RWMutex
	CoordinateH   int //人竖向坐标
	CoordinateW   int //横向坐标
	BoxCoordinate []Coordinate
	BoxSeat       []Coordinate
}

type Coordinate struct {
	H int
	W int
}

func init() {
	engine = &Box{
		Levels:        2,
		L:             &sync.RWMutex{},
		BoxCoordinate: make([]Coordinate, 0),
		BoxSeat:       make([]Coordinate, 0),
	}
	flush = make(chan int, 1)
	complete = make(chan int, 1)
}

//载入地图
func (b *Box) loadMap() error {
	b.L.Lock()
	defer b.L.Unlock()
	b.BoxSeat = []Coordinate{}
	b.BoxCoordinate = []Coordinate{}
	file, err := os.Open("./maps/" + cast.ToString(engine.Levels) + ".l")
	if err != nil {
		return errors.New("载入地图错误")
	}
	fmt.Fscanf(file, "%d %d", &b.H, &b.W)
	b.M = make([][]int, b.H)
	for h := 0; h < b.H; h++ {
		row := []int{}
		for w := 0; w < b.W; w++ {
			var num int
			fmt.Fscan(file, &num)
			row = append(row, num)
			if num == 2 {
				b.CoordinateH = h
				b.CoordinateW = w
			}
			if num == 3 {
				b.BoxCoordinate = append(b.BoxCoordinate, Coordinate{
					H: h,
					W: w,
				})
			}
			if num == 4 {
				b.BoxSeat = append(b.BoxSeat, Coordinate{
					H: h,
					W: w,
				})
			}
		}
		b.M[h] = row
	}
	return nil
}

//▒▇█▓▒░▓▩▦▧▤
//初始化地图
func (b *Box) initMap() {
	b.L.RLock()
	defer b.L.RUnlock()
	for _, v := range b.BoxCoordinate {
		b.M[v.H][v.W] = 3
	}
	for _, v := range b.BoxSeat {
		if b.M[v.H][v.W] != 2 && b.M[v.H][v.W] != 3 && b.M[v.H][v.W] != 5 && b.M[v.H][v.W] != 6 {
			b.M[v.H][v.W] = 4
		}
	}
	b.M[b.CoordinateH][b.CoordinateW] = 2
	for h := 0; h < b.H; h++ {
		for w := 0; w < b.W; w++ {
			switch b.M[h][w] {
			case 0:
				fmt.Printf("  ") //空地
			case 1:
				fmt.Printf("▇") //墙
			case 2:
				fmt.Printf("♘") //人
			case 3:
				fmt.Printf("▤") //箱子
			case 4:
				fmt.Printf("⊙") //箱子推放的终点
			}
		}
		fmt.Println()
	}
}

//键盘事件监听
func (b *Box) control() {
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyArrowUp: //小键盘向上
				b.move(b.CoordinateH, b.CoordinateW, b.CoordinateH-1, b.CoordinateW)
			case termbox.KeyArrowDown: //小键盘向下
				b.move(b.CoordinateH, b.CoordinateW, b.CoordinateH+1, b.CoordinateW)
			case termbox.KeyArrowLeft: //小键盘向左
				b.move(b.CoordinateH, b.CoordinateW, b.CoordinateH, b.CoordinateW-1)
			case termbox.KeyArrowRight: //小键盘向右
				b.move(b.CoordinateH, b.CoordinateW, b.CoordinateH, b.CoordinateW+1)
			case termbox.KeyF5: //F5 用来重置地图
				b.loadMap()
				flush <- 1
			case termbox.KeyEnd: //end结束游戏
				fmt.Println("1秒后关闭游戏")
				time.Sleep(time.Second * 1)
				os.Exit(1)
			}
		}
	}
}

/**
0 空地
1:墙
2:人
3:箱子
4:箱子存放点
5:人走到存放点
6:箱子推到存放点显示
*/
func (b *Box) move(s_h, s_w, e_h, e_w int) {
	b.L.Lock()
	defer b.L.Unlock()
	b.M[s_h][s_w] = 0
	//下一步是空
	if b.M[e_h][e_w] == 0 {
		b.CoordinateH = e_h
		b.CoordinateW = e_w
	}
	if b.M[e_h][e_w] == 4 {
		b.CoordinateH = e_h
		b.CoordinateW = e_w
	}
	//下一步是箱子
	if b.M[e_h][e_w] == 3 {
		var h2, w2, h3, w3 int
		switch {
		case e_h-s_h > 0: //向下
			h2, w2, h3, w3 = e_h, e_w, e_h+1, e_w
		case e_h-s_h < 0: //向上
			h2, w2, h3, w3 = e_h, e_w, e_h-1, e_w
		case e_w-s_w > 0: //向右
			h2, w2, h3, w3 = e_h, e_w, e_h, e_w+1
		case e_w-s_w < 0: //向左
			h2, w2, h3, w3 = e_h, e_w, e_h, e_w-1
		}
		if b.M[h3][w3] == 1 || b.M[h3][w3] == 3 {
			return
		}
		b.BoxCoordinate = []Coordinate{}
		for _, v := range b.BoxCoordinate {
			if v.H != h2 && v.W != w2 {
				b.BoxCoordinate = append(b.BoxCoordinate, v)
			}
		}
		b.BoxCoordinate = append(b.BoxCoordinate, Coordinate{
			H: h3,
			W: w3,
		})
		b.CoordinateH, b.CoordinateW = h2, w2
		b.M[h2][w2] = 0
	}
	flush <- 1
}

func (b *Box) complete() {
	b.L.RLock()
	defer b.L.RUnlock()
	num := 0
	for _, row := range b.M {
		for _, v := range row {
			if v == 4 {
				num++
			}
		}
	}
	if num == 0 {
		complete <- 1
	}
}

func main() {
	termbox.Init()
	if err := engine.loadMap(); err != nil {
		fmt.Println(err)
		return
	}
	engine.initMap()
	go engine.control() //监听按键操作
	for {
		select {
		case <-flush:
			//cmd := exec.Command("clear") //linux的清屏命令
			cmd := exec.Command("cmd", "/c", "cls") //如果是win使用该调命令
			cmd.Stdout = os.Stdout
			cmd.Run()
			engine.initMap()
			engine.complete()
		case <-complete:
			engine.Levels++
			if err := engine.loadMap(); err != nil {
				fmt.Println(err)
			}
			fmt.Println("恭喜通关按任意键进入下一关")
		}
	}
	termbox.Close()
}
